package com.vipboot.platform.system.crud.service.rbac;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.digest.BCrypt;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.toolkit.JoinWrappers;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.vipboot.framework.core.constant.Const;
import com.vipboot.framework.core.exception.ServiceException;
import com.vipboot.framework.core.util.AssertCheck;
import com.vipboot.framework.core.util.MapStructUtil;
import com.vipboot.framework.core.util.StreamUtil;
import com.vipboot.framework.core.util.StringUtil;
import com.vipboot.framework.mybatisplus.core.page.PageData;
import com.vipboot.framework.mybatisplus.core.page.PageQuery;
import com.vipboot.framework.mybatisplus.database.DataBaseHelper;
import com.vipboot.framework.satoken.helper.LoginHelper;
import com.vipboot.platform.system.crud.dao.*;
import com.vipboot.platform.system.crud.service.DataScopeCheckService;
import com.vipboot.platform.system.domain.bo.SysRoleBo;
import com.vipboot.platform.system.domain.bo.SysUserBo;
import com.vipboot.platform.system.domain.entity.SysDept;
import com.vipboot.platform.system.domain.entity.SysUser;
import com.vipboot.platform.system.domain.entity.SysUserPost;
import com.vipboot.platform.system.domain.entity.SysUserRole;
import com.vipboot.platform.system.domain.vo.RoleCheckedVo;
import com.vipboot.platform.system.domain.vo.SysRoleVo;
import com.vipboot.platform.system.domain.vo.SysUserVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;


@Slf4j
@RequiredArgsConstructor
@Service
public class SysUserService {
    private final DataScopeCheckService dataScopeCheckService;
    private final SysUserDao sysUserDao;
    private final SysUserPostDao sysUserPostDao;
    private final SysUserRoleDao sysUserRoleDao;
    private final SysDeptDao sysDeptDao;
    private final SysRoleDao sysRoleDao;

    private Wrapper<SysUser> buildQueryWrapper(SysUserBo user) {
        Map<String, Object> params = user.getParams();
        QueryWrapper<SysUser> wrapper = Wrappers.query();
        wrapper.eq("u.delete_flag", Const.StatusNormal)
                .eq(ObjectUtil.isNotNull(user.getUserId()), "u.user_id", user.getUserId())
                .like(StringUtil.isNotBlank(user.getUserName()), "u.user_name", user.getUserName())
                .eq(StringUtil.isNotBlank(user.getStatus()), "u.status", user.getStatus())
                .like(StringUtil.isNotBlank(user.getPhoneNumber()), "u.phone_number", user.getPhoneNumber())
                .between(params.get("beginTime") != null && params.get("endTime") != null,
                        "u.create_time", params.get("beginTime"), params.get("endTime"))
                .and(ObjectUtil.isNotNull(user.getDeptId()), w -> {
                    List<SysDept> deptList = sysDeptDao.list(new LambdaQueryWrapper<SysDept>()
                            .select(SysDept::getDeptId)
                            .apply(DataBaseHelper.findInSet(user.getDeptId(), "ancestors")));
                    List<Long> ids = StreamUtil.toList(deptList, SysDept::getDeptId);
                    ids.add(user.getDeptId());
                    w.in("u.dept_id", ids);
                }).orderByAsc("u.user_id");
        if (StringUtil.isNotBlank(user.getExcludeUserIds())) {
            wrapper.notIn("u.user_id", StringUtil.splitTo(user.getExcludeUserIds(), Convert::toLong));
        }
        return wrapper;
    }

    public PageData<SysUserVo> page(SysUserBo bo, PageQuery pageQuery) {
        Page<SysUserVo> page = sysUserDao.getBaseMapper().selectPageUserList(pageQuery.build(),
                this.buildQueryWrapper(bo));
        return PageData.build(page);
    }

    public SysUserVo getInfoById(Long userId) {
        SysUser entity = sysUserDao.getById(userId);
        AssertCheck.throwIfNull(entity, "用户不存在");

        SysUserVo vo = MapStructUtil.convert(entity, SysUserVo.class);
        vo.setRoleIds(this.listRoleIdByUserId(userId));
        vo.setPostIds(this.listPostIdByUserId(userId));
        return vo;
    }

    @Transactional
    public boolean save(SysUserBo bo) {

        dataScopeCheckService.checkDeptDataScope(bo.getDeptId());

        if (this.checkExistUserName(bo)) {
            throw new ServiceException("新增用户'" + bo.getUserName() + "'失败，登录账号已存在");
        }
        if (ObjectUtil.isNotEmpty(bo.getPhoneNumber()) && this.checkExistPhone(bo)) {
            throw new ServiceException("新增用户'" + bo.getUserName() + "'失败，手机号码已存在");
        }
        if (ObjectUtil.isNotEmpty(bo.getEmail()) && this.checkExistEmail(bo)) {
            throw new ServiceException("新增用户'" + bo.getUserName() + "'失败，邮箱账号已存在");
        }

//        if (TenantHelper.isEnable()) {
//            if (!tenantService.checkAccountBalance(TenantHelper.getTenantId())) {
//                return R.fail("当前租户下用户名额不足，请联系管理员");
//            }
//        }

        SysUser entity = MapStructUtil.convert(bo, SysUser.class);
        entity.setPassword(BCrypt.hashpw(entity.getPassword()));
        boolean saveFlag = sysUserDao.save(entity);
        if (saveFlag) {
            // 新增用户岗位关联
            this.saveUserPost(entity.getUserId(), bo.getPostIds());
            // 新增用户与角色关联
            this.saveUserRole(entity.getUserId(), bo.getRoleIds());
            return true;
        }
        return false;
    }


    @Transactional(rollbackFor = Exception.class)
    public boolean update(SysUserBo bo) {
        AssertCheck.throwIfNull(bo.getUserId(), "用户ID不能为空");

        Long userId = bo.getUserId();
        this.checkUserAllowed(userId);
        dataScopeCheckService.checkUserDataScope(userId);
        dataScopeCheckService.checkDeptDataScope(bo.getDeptId());

        if (this.checkExistUserName(bo)) {
            throw new ServiceException("修改用户'" + bo.getUserName() + "'失败，登录账号已存在");
        }
        if (ObjectUtil.isNotEmpty(bo.getPhoneNumber()) && this.checkExistPhone(bo)) {
            throw new ServiceException("修改用户'" + bo.getUserName() + "'失败，手机号码已存在");
        }
        if (ObjectUtil.isNotEmpty(bo.getEmail()) && this.checkExistEmail(bo)) {
            throw new ServiceException("修改用户'" + bo.getUserName() + "'失败，邮箱账号已存在");
        }
        // 删除用户与岗位关联
        this.removeUserPostByUserIds(List.of(userId));
        // 新增用户与岗位关联
        this.saveUserPost(userId, bo.getPostIds());

        // 删除用户与角色关联
        this.removeUserRoleByUserIds(List.of(userId));
        // 新增用户与角色关联
        this.saveUserRole(userId, bo.getRoleIds());

        SysUser entity = MapStructUtil.convert(bo, SysUser.class);
        return sysUserDao.updateById(entity);
    }

    public boolean removeByIds(List<Long> userIds) {

        if (CollUtil.contains(userIds, LoginHelper.getUserId())) {
            throw new ServiceException("选择用户包含自身，不能删除");
        }
        for (Long userId : userIds) {
            this.checkUserAllowed(userId);
            dataScopeCheckService.checkUserDataScope(userId);
        }
        // 删除用户与岗位关联
        this.removeUserPostByUserIds(userIds);
        // 删除用户与角色关联
        this.removeUserRoleByUserIds(userIds);

        //逻辑删除：UPDATE `sys_user` SET `del_flag` = 1 WHERE (`user_id` = 101810794781507584 ) AND `del_flag` = 0 AND `tenant_id` = 0
        return sysUserDao.removeByIds(userIds);
    }

    /**
     * 修改用户状态
     *
     * @param userId 用户ID
     * @param status 帐号状态
     * @return 结果
     */
    public boolean changeStatus(Long userId, String status) {
        AssertCheck.throwIfNull(userId, "用户ID不能为空");
        LambdaUpdateWrapper<SysUser> updateWrapper = Wrappers.lambdaUpdate();
        updateWrapper.set(SysUser::getStatus, status);
        updateWrapper.eq(SysUser::getUserId, userId);
        return sysUserDao.update(updateWrapper);
    }

    /**
     * 用户授权角色
     *
     * @param userId  用户ID
     * @param roleIds 角色组
     */
    @Transactional
    public void authRole(Long userId, List<Long> roleIds) {
        AssertCheck.throwIfNull(userId, "用户ID不能为空");
        AssertCheck.throwIfNull(roleIds, "授权角色不能为空");
        dataScopeCheckService.checkUserDataScope(userId);

        this.removeUserRoleByUserIds(List.of(userId));
        this.saveUserRole(userId, roleIds);
    }

    /**
     * 根据用户ID获取授权角色 和 查询角色集合
     */
    public RoleCheckedVo getAuthRole(Long userId) {
        List<SysRoleVo> roles = sysRoleDao.list(new SysRoleBo());
        List<Long> roleIds = this.listRoleIdByUserId(userId);
        RoleCheckedVo vo = new RoleCheckedVo();
        vo.setCheckedKeys(roleIds);
        vo.setList(roles);
        return vo;
    }

    /**
     * 重置用户密码
     *
     * @param userId   用户ID
     * @param password 密码
     * @return 结果
     */
    public boolean resetPwd(Long userId, String password) {
        AssertCheck.throwIfNull(userId, "用户ID不能为空");
        LambdaUpdateWrapper<SysUser> updateWrapper = Wrappers.lambdaUpdate();
        updateWrapper.set(SysUser::getPassword, BCrypt.hashpw(password));
        updateWrapper.eq(SysUser::getUserId, userId);
        return sysUserDao.update(updateWrapper);
    }

    /**
     * 通过用户ID查询岗位数量
     *
     * @param userId 用户ID
     * @return 结果
     */
    private List<Long> listPostIdByUserId(Long userId) {
        MPJLambdaWrapper<SysUserPost> lqw = JoinWrappers.lambda();
        lqw.select(SysUserPost::getPostId);
        lqw.eq(SysUserPost::getUserId, userId);
        return sysUserPostDao.selectJoinList(Long.class, lqw);
    }

    /**
     * 保存用户岗位信息关联
     *
     * @param userId  用户ID
     * @param postIds 新增的岗位ID列表
     */
    private boolean saveUserPost(Long userId, List<Long> postIds) {
        boolean inserted = true;
        List<SysUserPost> list = new ArrayList<>();
        Optional.ofNullable(postIds).orElse(List.of())
                .forEach(postId -> {
                    SysUserPost up = new SysUserPost();
                    up.setUserId(userId);
                    up.setPostId(postId);
                    list.add(up);
                });
        if (CollUtil.isNotEmpty(list)) {
            inserted = sysUserPostDao.saveBatch(list);
        }
        return inserted;
    }

    /**
     * 批量删除用户和岗位关联
     * delete from sys_user_post where user_id in
     *
     * @param userIds 需要删除的数据ID
     */
    private boolean removeUserPostByUserIds(List<Long> userIds) {
        LambdaQueryWrapper<SysUserPost> lqw = Wrappers.lambdaQuery();
        lqw.in(SysUserPost::getUserId, userIds);
        return sysUserPostDao.remove(lqw);
    }


    /**
     * 通过用户ID查询角色数量
     *
     * @param userId 用户ID
     * @return 结果
     */
    public List<Long> listRoleIdByUserId(Long userId) {
        MPJLambdaWrapper<SysUserRole> lqw = JoinWrappers.lambda();
        lqw.select(SysUserRole::getRoleId);
        lqw.eq(SysUserRole::getUserId, userId);
        return sysUserRoleDao.selectJoinList(Long.class, lqw);
    }

    /**
     * 保存用户和角色信息关联
     *
     * @param userId  用户ID
     * @param roleIds 需要新增的角色数据ID
     */
    private boolean saveUserRole(Long userId, List<Long> roleIds) {
        boolean inserted = true;
        List<SysUserRole> list = new ArrayList<>();
        Optional.ofNullable(roleIds).orElse(List.of())
                .forEach(roleId -> {
                    SysUserRole ur = new SysUserRole();
                    ur.setUserId(userId);
                    ur.setRoleId(roleId);
                    list.add(ur);
                });
        if (CollUtil.isNotEmpty(list)) {
            inserted = sysUserRoleDao.saveBatch(list);
        }
        return inserted;
    }

    /**
     * 批量删除用户和角色关联
     * delete from sys_user_role where user_id in
     *
     * @param userIds 用户ID列表
     */
    private boolean removeUserRoleByUserIds(List<Long> userIds) {
        LambdaQueryWrapper<SysUserRole> lqw = Wrappers.lambdaQuery();
        lqw.in(SysUserRole::getUserId, userIds);
        return sysUserRoleDao.remove(lqw);
    }

    /**
     * 校验用户是否允许操作
     *
     * @param userId 用户ID
     */
    private void checkUserAllowed(Long userId) {
        if (ObjectUtil.isNotNull(userId) && LoginHelper.isSuperAdmin(userId)) {
            throw new ServiceException("不允许操作超级管理员用户");
        }
    }

    /**
     * 校验用户名称是否唯一
     *
     * @param bo 用户信息
     */
    private boolean checkExistUserName(SysUserBo bo) {
        LambdaQueryWrapper<SysUser> lqw = Wrappers.lambdaQuery();
        lqw.eq(SysUser::getUserName, bo.getUserName());
        lqw.ne(ObjectUtil.isNotNull(bo.getUserId()), SysUser::getUserId, bo.getUserId());
        return sysUserDao.exists(lqw);
    }

    /**
     * 校验手机号码是否唯一
     *
     * @param bo 用户信息
     */
    private boolean checkExistPhone(SysUserBo bo) {
        LambdaQueryWrapper<SysUser> lqw = Wrappers.lambdaQuery();
        lqw.eq(SysUser::getPhoneNumber, bo.getPhoneNumber());
        lqw.ne(ObjectUtil.isNotNull(bo.getUserId()), SysUser::getUserId, bo.getUserId());
        return sysUserDao.exists(lqw);
    }

    /**
     * 校验email是否唯一
     *
     * @param bo 用户信息
     */
    private boolean checkExistEmail(SysUserBo bo) {
        LambdaQueryWrapper<SysUser> lqw = Wrappers.lambdaQuery();
        lqw.eq(SysUser::getEmail, bo.getEmail());
        lqw.ne(ObjectUtil.isNotNull(bo.getUserId()), SysUser::getUserId, bo.getUserId());
        return sysUserDao.exists(lqw);
    }

}



